﻿//-----------------------------------------------------------------------
// <copyright file="ConsoleViewModel.cs" author="Artak Mkrtchyan">
//     Creative Commons - GNU General Public License (http://creativecommons.org/licenses/GPL/2.0/
// </copyright>
// <author>Artak Mkrtchyan</author>
// <email>mkArtak@gmail.com</email>
// <date>12/08/2012</date>
//-----------------------------------------------------------------------
using ConsoleHoster.Common.Model;
using ConsoleHoster.Common.ViewModel;
using ConsoleHoster.Model;
using ConsoleHoster.Model.Entities;
using ConsoleHoster.ViewModel.ControlVM;
using ConsoleHoster.ViewModel.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;

namespace ConsoleHoster.ViewModel
{
    /// <summary>
    /// ConsoleViewModel class's IConsoleVM implementation
    /// </summary>
    public partial class ConsoleViewModel : IConsoleVM
    {
        private const string FavoritesGroupName = "Favorites";

        public event ConsoleViewModelEventHandler Closing;
        public event ConsoleViewModelEventHandler PersistChangesRequest;

        private CommandHandlingManager commandManager = null;
        private FontSettings currentFontSettings;

        private string uniqueName = null;

        #region Public methods
        public void RunCommand(string argCommand)
        {
            if (this.ErrorMessage != null)
            {
                this.ErrorMessage = null;
            }

            if (argCommand != null)
            {
                if (!this.TryExecuteShortcutCommand(argCommand))
                {
                    bool tmpProcessed = this.commandManager.PreProcessCommand(argCommand);

                    if (!tmpProcessed)
                    {
                        bool tmpActionSucceeded = this.RunActionWithErrorHandling(() =>
                            {
                                this.Logger.LogMessage("Running command: {0}", argCommand);
                                this.underlyingProcess.SendData(argCommand);

                                if (!String.IsNullOrWhiteSpace(argCommand) && (!this.CommandHistory.Any() || !String.Equals(this.CommandHistory.Last(), argCommand, StringComparison.InvariantCultureIgnoreCase)))
                                {
                                    this.CommandHistory.Add(argCommand);
                                    this.commandHistoryIndex = this.CommandHistory.Count;
                                }
                            });

                        if (tmpActionSucceeded)
                        {
                            this.commandManager.PostProcessCommand(argCommand);
                        }
                    }
                }
            }
        }

        public void AddFavoriteCommand(string argName, string argCommandText)
        {
            this.RunActionWithErrorHandling(() =>
                {
                    if (this.Project.Commands.Any(item => item.GroupName.Equals(FavoritesGroupName, StringComparison.InvariantCultureIgnoreCase) && item.Name.Equals(argName, StringComparison.InvariantCultureIgnoreCase)))
                    {
                        this.Logger.LogWarning("A favorite command with the same name already exists. Project: {0}, Command: {1}", this.Name, argName);
                        this.ErrorMessage = "Favorite already exist";
                    }
                    else
                    {
                        this.Project.Commands.Add(new CommandDataViewModel(new CommandData
                        {
                            CommandText = argCommandText,
                            GroupName = FavoritesGroupName,
                            IsFinal = true,
                            Name = argName
                        }));
                        this.OnPersistChangesRequest();
                        this.NotifyAvailableCommandsChange();
                    }
                });
        }

        public bool HandleKeyboardInput(Key argHitKey, string argContext)
        {
            bool tmpResult = false;
            this.RunActionWithErrorHandling(() =>
                {
                    tmpResult = this.KeyboardInputHandler.HandleKeyboardInput(argHitKey, argContext);
                }, String.Format("Failed to handle keyboard input for key: '{0}', and context: '{1}'", argHitKey, argContext));
            return tmpResult;
        }

        public void AppendCommand(string argCommandPart)
        {
            this.CurrentCommand += argCommandPart;
        }

        public void Close()
        {
            this.Logger.LogMessage("Closing...");
            this.RunActionWithErrorHandling(() =>
                {
                    // Notify the host about closing intention
                    this.OnClosing();

                    // Cleanup background processes and the rest
                    this.Dispose();
                }, "Error while closing project");
            this.Logger.LogMessage("Successfully closed project");
        }

        public void Dispose()
        {
            this.RunActionWithErrorHandling(() =>
            {
                this.Logger.LogMessage("Disposing...");
                ProcessWrapper tmpUnderlyingProcess = this.underlyingProcess;
                if (tmpUnderlyingProcess != null)
                {

                    ProcessPerfCounterViewModel tmpCpuUsageVM = this.CpuUsageVM;
                    if (tmpCpuUsageVM != null)
                    {
                        tmpCpuUsageVM.Dispose();
                    }
                    tmpUnderlyingProcess.Dispose();

                    this.outputLogger.Dispose();

                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
                }

                this.Logger.LogMessage("Disposal Finished !");
            });
        }
        #endregion

        #region Helper methods
        private void OnClosing()
        {
            ConsoleViewModelEventHandler temp = this.Closing;
            if (temp != null)
            {
                temp(this);
            }
        }

        private void OnPersistChangesRequest()
        {
            ConsoleViewModelEventHandler temp = this.PersistChangesRequest;
            if (temp != null)
            {
                temp(this);
            }
        }

        private bool TryExecuteShortcutCommand(string argCommand)
        {
            bool tmpResult = false;

            if (!String.IsNullOrWhiteSpace(argCommand) && argCommand.StartsWith("^") && argCommand.EndsWith("^"))
            {
                try
                {
                    string tmpMatchingCommand = argCommand.Substring(1, argCommand.Length - 2);
                    IEnumerable<CommandDataViewModel> tmpCommandsSource = this.Project.Commands;

                    if (this.TryFindAndExecuteCommand(this.Project.Commands, tmpMatchingCommand) || this.TryFindAndExecuteCommand(this.GlobalCommands, tmpMatchingCommand))
                    {
                        tmpResult = true;
                    }
                }
                catch (Exception ex)
                {
                    this.Logger.LogError(ex.ToString());
                }
            }

            return tmpResult;
        }

        private bool TryFindAndExecuteCommand(IEnumerable<CommandDataViewModel> argList, string argCommand)
        {
            bool tmpResult = false;

            CommandDataViewModel tmpCommand = argList.Where(item => item.Name.Equals(argCommand, StringComparison.InvariantCultureIgnoreCase)).SingleOrDefault();
            if (tmpCommand != null)
            {
                this.ExecuteCommand(tmpCommand);
                tmpResult = true;
            }
            return tmpResult;
        }
        #endregion

        #region Properties
        public string Name
        {
            get
            {
                return this.uniqueName ?? this.Project.Name;
            }
            set
            {
                this.uniqueName = value;
            }
        }

        public string OutputLogPath
        {
            get
            {
                return this.outputLogPath;
            }
        }

        public string UIFriendlyName
        {
            get
            {
                return this.Name.Replace("_", "__");
            }
        }

        public string CurrentFolder
        {
            get
            {
                return this.currentFolder;
            }
            private set
            {
                if (value != this.currentFolder)
                {
                    this.currentFolder = value;
                    this.NotifyPropertyChange("CurrentFolder");
                    this.OnCurrentFolderChanged();
                }
            }
        }

        public string CurrentCommand
        {
            get
            {
                return this.currentCommand;
            }
            set
            {
                if (value != this.currentCommand)
                {
                    string tmpOldValue = this.currentCommand;
                    this.currentCommand = value;
                    this.OnCurrentCommandChanged(tmpOldValue);
                    this.NotifyPropertyChange("CurrentCommand");
                }
            }
        }

        public ICommandHandlingManager CommandManager
        {
            get
            {
                return this.commandManager;
            }
        }

        public FontSettings CurrentFontSettings
        {
            get
            {
                return this.currentFontSettings;
            }
            set
            {
                this.currentFontSettings = value;
                this.NotifyPropertyChange("CurrentFontSettings");
            }
        }
        #endregion
    }
}
